package com.lotso.web.common.utils;

import com.google.common.base.Strings;

import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Year;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;

/**
 * ClassName: Dates
 * Description: 日期工具
 * Date: 2018/10/23 16:27 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
public class DateUtil {

    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final String MONTH_PATTERN = "yyyy-MM";
    public static final String TIME_PATTERN = "HH:mm:ss";
    public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    public static Date now() {
        return new Date();
    }

    /**
     * 当前年
     */
    public static final int THIS_YEAR = Year.now().getValue();
    /**
     * 当前月
     */
    public static final Month THIS_MONTH = LocalDate.now().getMonth();

    public static final int THIS_MONTH_VALUE = LocalDate.now().getMonth().getValue();
    /**
     * 当前季度的第一个月份
     */
    public static final int FIRST_MONTH_OF_THIS_QUARTER = LocalDate.now().getMonth().firstMonthOfQuarter().getValue();

    /**
     * 当前季度的最后一个月份
     */
    public static final int LAST_MONTH_OF_THIS_QUARTER = Month.of(FIRST_MONTH_OF_THIS_QUARTER + 2).getValue();


    /**
     * Date 转为 LocalDateTime
     *
     * @param date 基准日期，天数
     * @return
     */
    public static LocalDateTime toLocalDateTime(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * Date 转为 LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate toLocalDate(final Date date) {
        return toLocalDateTime(date).toLocalDate();
    }

    /**
     * LocalDateTime 转为 Date
     *
     * @param localDateTime
     * @return
     */
    public static Date toDate(LocalDateTime localDateTime) {
        ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(localDateTime);
        Instant instant = localDateTime.toInstant(offset);
        return Date.from(instant);
    }

    /**
     * LocalDate 转为 LocalDateTime
     *
     * @param localDate
     * @return
     */
    public static LocalDateTime toLocatDateTime(LocalDate localDate) {
        return LocalDateTime.of(localDate, LocalTime.MIN);
    }


    /**
     * 某月所在季度的第一个月
     *
     * @param month
     * @return
     */
    public static Month firstMonthOfQuarter(final int month) {
        return Month.of(month).firstMonthOfQuarter();
    }

    /**
     * 某月所在季度的最后一个月
     *
     * @param month
     * @return
     */
    public static Month lastMonthOfQuarter(final int month) {
        return Month.of(firstMonthOfQuarter(month).getValue() + 2);
    }

    /**
     * 某一年的第一天
     *
     * @param year
     * @return
     */
    public static LocalDate firstDayOfYear(final int year) {
        return LocalDate.of(year, Month.JANUARY, 1);
    }

    /**
     * 某一年的最后一天
     *
     * @param year
     * @return
     */
    public static LocalDate lastDayOfYear(final int year) {
        return LocalDate.of(year, Month.DECEMBER, 31);
    }


    /**
     * 日期转换为字符串，默认格式为 yyyy-MM-dd
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String dateToString(final Date date, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_PATTERN : pattern;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return toLocalDateTime(date).format(formatter);
    }

    /**
     * 日期转换为字符串，默认格式为 yyyy-MM-dd
     *
     * @param date
     * @return
     */
    public static String dateToString(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate().toString();
    }

    /**
     * 日期转换为字符串
     *
     * @param localDateTime
     * @param pattern
     * @return
     */
    public static String dateToString(final LocalDateTime localDateTime, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_PATTERN : pattern;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return localDateTime.format(formatter);
    }

    /**
     * 字符串转换为Date(支持日期),不支持时间部分的转换
     *
     * @param strDate
     * @return
     */
    public static Date stringToDate(final String strDate, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_PATTERN : pattern;
        LocalDate localDate = stringToLocalDate(strDate, pattern);
        return toDate(toLocatDateTime(localDate));
    }

    /**
     * 字符串转换为Date(支持时间)，不支持单日期的转换
     *
     * @param strDate 必须为日期 + 时间
     * @return
     */
    public static Date stringToTime(final String strDate, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_TIME_PATTERN : pattern;
        LocalDateTime localDateTime = stringToLocalDateTime(strDate, pattern);
        return toDate(localDateTime);
    }


    public static LocalDate stringToLocalDate(final String strDate, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_PATTERN : pattern;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(strDate, formatter);
    }

    public static LocalDateTime stringToLocalDateTime(final String strDate, String pattern) {
        pattern = Strings.isNullOrEmpty(pattern) ? DATE_TIME_PATTERN : pattern;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(strDate, formatter);
    }


    /**
     * 计算日期，加、减去多少天
     *
     * @param date   基准日期
     * @param amount 支持正负数，天数
     * @return
     */
    public static LocalDate minusDays(final Date date, final int amount) {
        LocalDateTime localDateTime = toLocalDateTime(date);
        return localDateTime.toLocalDate().minusDays(amount);
    }

    /**
     * 计算日期，加、减去多少天
     *
     * @param date   基准日期
     * @param amount 支持正负数，天数
     * @return
     */
    public static String minusDay(final Date date, final int amount) {
        return minusDays(date, amount).toString();
    }

    /**
     * 计算日期，加、减去几个月
     *
     * @param date   基准日期
     * @param amount 支持正负数，月份数
     * @return
     */
    public static LocalDate minusMonths(final Date date, final int amount) {
        LocalDateTime localDateTime = toLocalDateTime(date);
        return localDateTime.toLocalDate().minusMonths(amount);
    }

    /**
     * 计算日期，加、减去几个月
     *
     * @param date   基准日期
     * @param amount 支持正负数，月份数
     * @return
     */
    public static String minusMonth(final Date date, final int amount) {
        return minusMonths(date, amount).toString();
    }

    /**
     * 计算日期，加、减去几个星期
     *
     * @param date   基准日期
     * @param amount 支持正负数，星期数
     * @return
     */
    public static LocalDate minusWeeks(final Date date, final int amount) {
        LocalDateTime localDateTime = toLocalDateTime(date);
        return localDateTime.toLocalDate().minusWeeks(amount);
    }

    /**
     * 计算日期，加、减去几个星期
     *
     * @param date   基准日期
     * @param amount 支持正负数，星期数
     * @return
     */
    public static String minusWeek(final Date date, final int amount) {
        return minusWeeks(date, amount).toString();
    }

    /**
     * 设置日期，用于获取指定日期该月份的随意天
     *
     * @param date
     * @param amount
     * @return String
     */
    public static String setDay(final Date date, final int amount) {
        return setDays(date, amount).toString();
    }

    /**
     * 设置日期，用于获取指定日期该月份的随意天
     *
     * @param date
     * @param amount
     * @return LocalDate
     */
    public static LocalDate setDays(final Date date, final int amount) {
        return toLocalDateTime(date).withDayOfMonth(amount).toLocalDate();
    }

    /**
     * 设置日期，用于获取指定日期该年的随意月份
     *
     * @param date
     * @param amount
     * @return String
     */
    public static String setMonth(final Date date, final int amount) {
        return setMonths(date, amount).toString();
    }

    /**
     * 设置日期，用于获取指定日期该年的随意月份
     *
     * @param date
     * @param amount
     * @return LocalDate
     */
    public static LocalDate setMonths(final Date date, final int amount) {
        return toLocalDateTime(date).withMonth(amount).toLocalDate();
    }

    /**
     * 某日期所在月的第一天
     *
     * @param date
     * @return
     */
    public static String firstDayOfMonth(Date date) {
        return toLocalDateTime(date).with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().toString();
    }

    /**
     * 所在日期上个月的第一天
     *
     * @param date
     * @return
     */
    public static String firstDayOfLastMonth(Date date) {
        return toLocalDateTime(date).with(TemporalAdjusters.firstDayOfMonth()).minusMonths(1).toLocalDate().toString();
    }

    /**
     * 某日期所在月的最后一天
     *
     * @param date
     * @return
     */
    public static String lastDayOfMonth(Date date) {
        return toLocalDateTime(date).with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().toString();
    }

    /**
     * 某日期所在年的第一天
     *
     * @param date
     * @return
     */
    public static String firstDayOfYear(Date date) {
        return toLocalDateTime(date).with(TemporalAdjusters.firstDayOfYear()).toLocalDate().toString();
    }

    /**
     * 某日期所在年的最后一天
     *
     * @param date
     * @return
     */
    public static String lastDayOfYear(Date date) {
        return toLocalDateTime(date).with(TemporalAdjusters.lastDayOfYear()).toLocalDate().toString();
    }

    /**
     * 某日期所在月的第几个星期几，会跨月计算
     *
     * @param date
     * @param amount
     * @param dayOfWeek
     * @return
     */
    public static String dayOfWeekInMonth(Date date, int amount, DayOfWeek dayOfWeek) {
        return toLocalDateTime(date).with(TemporalAdjusters.dayOfWeekInMonth(amount, dayOfWeek)).toLocalDate().toString();
    }

    /**
     * 下一个工作日
     *
     * @param date
     * @return
     */
    public static String nextWorkday(Date date) {
        LocalDateTime localDateTime = date == null ? LocalDateTime.now() : toLocalDateTime(date);
        return localDateTime.with(new TemporalAdjuster() {
            @Override
            public Temporal adjustInto(Temporal temporal) {

                LocalDateTime localDate = (LocalDateTime) temporal;
                do {
                    localDate = localDate.plusDays(1);
                } while (localDate.getDayOfWeek().getValue() >= DayOfWeek.SATURDAY.getValue());
                return localDate;
            }
        }).toLocalDate().toString();
    }

    /**
     * 下一个工作日
     *
     * @return
     */
    public static String nextWorkday() {
        return nextWorkday(null);
    }

    public static String firstMonthOfQuarter(int year, int quarter) {
        String date;
        switch (quarter) {
            case 1:
                date = year + "-" + Month.JANUARY.getValue();
                break;
            case 2:
                date = year + "-" + Month.APRIL.getValue();
                break;
            case 3:
                date = year + "-" + Month.JULY.getValue();
                break;
            case 4:
                date = year + "-" + Month.OCTOBER.getValue();
                break;
            default:
                date = year + "-" + Month.JANUARY.getValue();
                break;
        }
        return date;
    }

    public static String lastMonthOfQuarter(int year, int quarter) {
        String date;
        switch (quarter) {
            case 1:
                date = year + "-" + Month.MARCH.getValue();
                break;
            case 2:
                date = year + "-" + Month.JUNE.getValue();
                break;
            case 3:
                date = year + "-" + Month.SEPTEMBER.getValue();
                break;
            case 4:
                date = year + "-" + Month.DECEMBER.getValue();
                break;
            default:
                date = year + "-" + Month.MARCH.getValue();
                break;
        }
        return date;
    }

    public static String firstMonthOfQuarter(Date date) {
        LocalDateTime localDateTime = toLocalDateTime(date);
        LocalDateTime dateTime = localDateTime.withMonth(localDateTime.getMonth().firstMonthOfQuarter().getValue());
        return dateToString(dateTime, MONTH_PATTERN);
    }

    public static String lastMonthOfQuarter(Date date) {
        LocalDateTime localDateTime = toLocalDateTime(date);
        LocalDateTime dateTime = localDateTime.withMonth(localDateTime.getMonth().firstMonthOfQuarter().getValue() + 2);
        return dateToString(dateTime, MONTH_PATTERN);
    }

    public static Date yesterday(Date date) {
        return stringToDate(minusDay(date, 1), DATE_PATTERN);
    }

    public static Date yesterday() {
        return stringToDate(minusDay(now(), 1), DATE_PATTERN);
    }
}
